/* **************************************************
 * User Group Manager
 *      author:     Baskin Tapkan
 *          written for Code Plex
 *              April - 2008
 *                  http://tapkan.com
 * ****************************************************
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Management;
using System.DirectoryServices;

namespace UserGroupManager
{
    public class UserMgr : IUserMgr
    {
        private string computerName;
        private DataSet userInfo;

        //[Flags]
        public enum AdsUserFlags
        {
            Script = 1,                                     // 0x1
            AccountDisabled = 2,                            // 0x2
            HomeDirectoryRequired = 8,                      // 0x8 
            AccountLockedOut = 16,                          // 0x10
            PasswordNotRequired = 32,                       // 0x20
            PasswordCannotChange = 64,                      // 0x40
            EncryptedTextPasswordAllowed = 128,             // 0x80
            TempDuplicateAccount = 256,                     // 0x100
            NormalAccount = 512,                            // 0x200
            InterDomainTrustAccount = 2048,                 // 0x800
            WorkstationTrustAccount = 4096,                 // 0x1000
            ServerTrustAccount = 8192,                      // 0x2000
            PasswordDoesNotExpire = 65536,                  // 0x10000
            MnsLogonAccount = 131072,                       // 0x20000
            SmartCardRequired = 262144,                     // 0x40000
            TrustedForDelegation = 524288,                  // 0x80000
            AccountNotDelegated = 1048576,                  // 0x100000
            UseDesKeyOnly = 2097152,                        // 0x200000
            DontRequirePreauth = 4194304,                   // 0x400000
            PasswordExpired = 8388608,                      // 0x800000
            TrustedToAuthenticateForDelegation = 16777216,  // 0x1000000
            NoAuthDataRequired = 33554432                   // 0x2000000
        }

        public string ComputerName
        {
            get { return computerName; }
            set { computerName = value; }
        }

        /// <summary>
        /// Gets all local users, returns the results in a dataset
        /// </summary>
        /// <returns></returns>
        public DataSet GetAllLocalUsers()
        {
            CreateUserInfoTable();      // create the info table
            FetchAllUsers();            // fetch all the users
            return userInfo;            // return the dataset userInfo
        }

       
        //TODO : Implement Get Groups by User
        public DataSet GetGroupsByUser(string userName)
        {
            DataSet ds = new DataSet();

            return ds;
        }

        /// <summary>
        /// Gets a dataset of locked users
        /// </summary>
        /// <returns></returns>
        public DataSet GetLockedUsers()
        {
            CreateUserInfoTable();          // initialize the userInfo table/dataset
            FetchLockedUsers();             // get a list of locked users
            return userInfo;
        }

        /// <summary>
        /// Creates a new local user in the target machine
        /// </summary>
        /// <param name="lu"></param>
        /// <returns></returns>
        public string CreateNewAccount(LocalUser lu)
        {
            string result = "";

            string dirEntryString = "WinNT://" + computerName + ",computer";
            // check if the network path is valid
            if (!CheckforValidDirectoryEntry(dirEntryString))
            {
                result = "The network path is not found";
                return result;
            }

            DirectoryEntry dirEntry;
            // set the directory entry construct
            dirEntry = new DirectoryEntry(dirEntryString);

            if (CheckForExistingUser(lu, dirEntry))
            {
                result = string.Format("Account {0} already exists on target system",
                    lu.UserName);
                dirEntry.Dispose();
                ErrorLogger.LogError(result, "no trace needed");
                return result;
            }

            try
            {
                // create a new user
                DirectoryEntry de = dirEntry.Children.Add(lu.UserName, "user");

                // set  properties
                de.Invoke("SetPassword", new object[] { lu.UserPswd });
                de.Invoke("Put", new Object[] { "FullName", lu.UserFullName });
                de.Invoke("Put", new object[] { "Description", lu.UserDescription });

                int combinedFlag = 0;

                if (!lu.PasswordLastSet)
                {
                    // this sets the "user must change password on next logon" flag
                    de.InvokeSet("PasswordExpired", 1);
                    //Flags
                    //1. User cannot change password
                    int ADS_UF_PASSWD_CANT_CHANGE = 0x000000040;
                    //newUser.Invoke("Put", new Object[] { "userFlags", ADS_UF_PASSWD_CANT_CHANGE });

                    //2. Password Never Expires
                    int ADS_UF_DONT_EXPIRE_PASSWD = 0x00010000;
                    //newUser.Invoke("Put", new Object[] { "userFlags", ADS_UF_DONT_EXPIRE_PASSWD });

                    bool cannotchangepassword = !lu.PasswordChangeable;
                    bool passwordneverexpires = !lu.PasswordExpires;
                    if (cannotchangepassword && passwordneverexpires)
                        combinedFlag = ADS_UF_DONT_EXPIRE_PASSWD | ADS_UF_PASSWD_CANT_CHANGE;
                    else if (cannotchangepassword)
                        combinedFlag = ADS_UF_PASSWD_CANT_CHANGE;
                    else if (passwordneverexpires)
                        combinedFlag = ADS_UF_DONT_EXPIRE_PASSWD;
                }
                else
                {
                    // this sets the "user must change password on next logon" flag
                    de.InvokeSet("PasswordExpired", -1);
                }

                // check if the account is disabled
                if (lu.AccountDisabled)
                {
                    // set the appropriate flag - 
                    // -> borrowed from "Windows OS User Management By Ritesh_Singh" (CodeProject)
                    int ADS_UF_ACCOUNTDISABLE = 0x0002;
                    combinedFlag = combinedFlag | ADS_UF_ACCOUNTDISABLE;
                }

                de.Invoke("Put", new Object[] { "userFlags", combinedFlag });

                // commit the changes
                de.CommitChanges();
                de.Dispose();
                result = string.Format("Account {0} created successfully", lu.UserName);
            }
            catch (Exception ex)
            {
                dirEntry.Dispose();
                // handle the exception
                result = ex.Message;
                // throw ex;
                // ErrorLogger.LogError(ex.Message, ex.StackTrace);
            }
            return result;
        }

        /// <summary>
        /// Deletes a local account - use it cautiously!
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool DeleteAccount(string userName)
        {
            bool result = false;
            string dirEntryString = "WinNT://" + computerName + ",computer";

            if (!CheckforValidDirectoryEntry(dirEntryString))
            {
                result = false;
                return result;
            }

            try
            {
                // create a new directory entry object 
                DirectoryEntry dirEntry =
                        new DirectoryEntry(dirEntryString);

                // locate the userEntry node in the created directory object
                DirectoryEntry userEntry = dirEntry.Children.Find(userName, "user");

                // remove the found entry from the nodes
                dirEntry.Children.Remove(userEntry);
                dirEntry.CommitChanges();
                userEntry.Dispose();
                dirEntry.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                result = false;
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
            }

            return result;
        }

        /// <summary>
        /// Edits a local account, takes in two objects in order to reflect changes
        /// </summary>
        /// <param name="oldUser"></param>
        /// <param name="newUser"></param>
        /// <returns>result as string: status of the operation </returns>
        public string EditAccount(LocalUser oldUser, LocalUser newUser)
        {
            string result = String.Empty;

            string dirEntryString = "WinNT://" + computerName + ",computer";

            if (!CheckforValidDirectoryEntry(dirEntryString))
            {
                result = "The network path is not found";
                return result;
            }

            try
            {
                DirectoryEntry dirEntry = new DirectoryEntry(dirEntryString);

                DirectoryEntry de = dirEntry.Children.Find(oldUser.UserName, "user");

                de.Properties["fullName"].Value = newUser.UserFullName;
                de.Properties["description"].Value = newUser.UserDescription;

                int combinedFlag = 0;

                if (!newUser.PasswordLastSet)
                {
                    // this sets the "user must change password on next logon" flag
                    de.InvokeSet("PasswordExpired", 1);
                    //Flags
                    //1. User cannot change password
                    int ADS_UF_PASSWD_CANT_CHANGE = 0x000000040;
                    //newUser.Invoke("Put", new Object[] { "userFlags", ADS_UF_PASSWD_CANT_CHANGE });

                    //2. Password Never Expires
                    int ADS_UF_DONT_EXPIRE_PASSWD = 0x00010000;
                    //newUser.Invoke("Put", new Object[] { "userFlags", ADS_UF_DONT_EXPIRE_PASSWD });

                    bool cannotchangepassword = !newUser.PasswordChangeable;
                    bool passwordneverexpires = !newUser.PasswordExpires;

                    if (cannotchangepassword && passwordneverexpires)
                        combinedFlag = ADS_UF_DONT_EXPIRE_PASSWD | ADS_UF_PASSWD_CANT_CHANGE;
                    else if (cannotchangepassword)
                        combinedFlag = ADS_UF_PASSWD_CANT_CHANGE;
                    else if (passwordneverexpires)
                        combinedFlag = ADS_UF_DONT_EXPIRE_PASSWD;
                }
                else
                {
                    // this sets the "user must change password on next logon" flag
                    de.InvokeSet("PasswordExpired", -1);
                }

                // check if the account is disabled
                if (newUser.AccountDisabled)
                {
                    // set the appropriate flag - 
                    // -> borrowed from "Windows OS User Management By Ritesh_Singh" (CodeProject)
                    int ADS_UF_ACCOUNTDISABLE = 0x0002;
                    combinedFlag = combinedFlag | ADS_UF_ACCOUNTDISABLE;
                }

                if ((oldUser.AccountLocked) && (!newUser.AccountLocked))
                {
                    // this kind of call is now possible under .NET framework 2.0
                    de.InvokeSet("IsAccountLocked", false);
                    //Console.WriteLine("Account locked: {0}", userEntry.InvokeGet("IsAccountLocked"));
                }
                de.Invoke("Put", new Object[] { "userFlags", combinedFlag });

                de.CommitChanges();

                #region "experimental code to change account name - keep for reference"

                // .NET Framework 3.5 code sampe
                //PrincipalContext pc = new PrincipalContext(ContextType.Machine);
                //System.DirectoryServices.AccountManagement.UserPrincipal u = new UserPrincipal(pc);
                //u.SetPassword(password);
                //u.Name = username;
                //u.Description = description;
                //u.UserCannotChangePassword = true;
                //u.PasswordNeverExpires = true;
                //u.Save();

                // use it for deleting entry 
                //  dirEntry.Children.Remove(userEntry);

                // userEntry.Invoke("Put", new object[] { "UserName", newUser.UserName });

                // valid for active directory type entries, try something else
                // userEntry.Rename(newUser.UserName);

                // runs, but does not change the username !!! - unbelievable !!!
                // userEntry.Username = newUser.UserName;

                // userEntry.DeleteTree();
                // throws an Exception ==>> Exception from HRESULT: 0x8000500F
                //userEntry.Properties["Name"].Value = newUser.UserName;

                #endregion

                // see if the account's user name has changed, 
                if (!oldUser.UserName.Equals(newUser.UserName))
                {
                    // then change the user name using WMI
                    ManagementObject theInstance =
                        new ManagementObject("root\\CIMv2", "Win32_UserAccount.Domain='" + computerName +
                        "',Name='" + oldUser.UserName + "'", null);

                    ManagementBaseObject inputParams = theInstance.GetMethodParameters("Rename");
                    inputParams.SetPropertyValue("Name", newUser.UserName);
                    ManagementBaseObject outParams = theInstance.InvokeMethod("Rename", inputParams, null);
                }
                de.Dispose();
                result = string.Format("Account {0} edited successfully", newUser.UserName);
            }
            // cascading error handling
            catch (System.Reflection.TargetInvocationException exTIX)
            {
                ErrorLogger.LogError(exTIX.InnerException.Message, exTIX.InnerException.StackTrace);
            }
            catch (System.Runtime.InteropServices.COMException exCom)
            {
                ErrorLogger.LogError(exCom.InnerException.Message, exCom.InnerException.StackTrace);
            }
            catch (Exception ex)
            {
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
                //System.Runtime.InteropServices.COMException COMEx =
                //     (System.Runtime.InteropServices.COMException) ex;
                //Console.WriteLine(COMEx.ErrorCode);
            }
            return result;
        }

        /// <summary>
        /// Unlocks a user account
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool UnlockAccount(string userName)
        {
            bool result = false;

            try
            {
                DirectoryEntry dirEntry =
                    new DirectoryEntry("WinNT://" + computerName + ",computer");

                DirectoryEntry userEntry = dirEntry.Children.Find(userName, "user");
                // new method available in .NET framework 2.0
                userEntry.InvokeSet("IsAccountLocked", false);
                userEntry.CommitChanges();
                userEntry.Dispose();
                dirEntry.Dispose();
                result = true;
            }
            catch (Exception ex)
            {
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
            }

            return result;
        }

        #region "Lock account attempt"
        /// <summary>
        /// THIS IS NOT POSSIBLE for WinNT Provider -- see commented out code
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        //public bool LockAccount(string userName)
        //{
        //    bool result = false;
        /*
         * 
         * Account Lockout (WinNT Provider)
         *
         *   When the number of failed logon attempts is exceeded, the user account becomes 
         *  locked out for the number of minutes specified by the lockoutDuration attribute. 
         * The IADsUser.IsAccountLocked property appears to be the property to use to read and
         * modify the lockout state of a user account, but the WinNT ADSI provider has 
         * restrictions that limit the use of the IsAccountLocked property.
         *             
         * Resetting the Account Lockout Status
         * 
         * When using the WinNT provider, the IsAccountLocked property can only be set to FALSE, 
         * which unlocks the account. Attempting to set the IsAccountLocked property to TRUE will fail. 
         * Only the system can lock an account.
*/

        //try
        //{
        //    DirectoryEntry dirEntry =
        //        new DirectoryEntry("WinNT://" + computerName + ",computer");

        //    DirectoryEntry de = dirEntry.Children.Find(userName, "user");
        //    // new method available in .NET framework 2.0
        //    int combinedFlags = 0;
        //    combinedFlags = (int)de.InvokeGet("UserFlags");
        //    Console.WriteLine(combinedFlags.ToString());
        //    combinedFlags = combinedFlags | (int) AdsUserFlags.AccountLockedOut;
        //    Console.WriteLine(combinedFlags.ToString());
        //    de.Properties["BadPasswordAttempts"].Value = 5;
        //    de.Invoke("Put", new Object[] { "UserFlags", combinedFlags });

        //    de.CommitChanges();
        //    result = true;
        //}
        //catch (Exception ex)
        //{
        //    throw ex;
        //}
        //    return result;
        //}

        #endregion
        /// <summary>
        /// Resets the password of a selected account
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="pswd"></param>
        /// <returns></returns>
        public bool ResetPassword(string userName, string pswd)
        {
            bool result = false;

            try
            {
                // instantiate the main directory entry for SAM
                DirectoryEntry dirEntry =
                        new DirectoryEntry("WinNT://" + computerName + ",computer");
                // get user's account node
                DirectoryEntry userEntry = dirEntry.Children.Find(userName, "user");

                userEntry.Invoke("SetPassword", new Object[] { pswd });

                //  object[] password = new object[] { "mydummy01", pswd };
                // invoke the set password method -- needs further work
                //     userEntry.Invoke("SetPassword", new string[] { pswd });
                //  userEntry.Invoke("ChangePassword", password);

                userEntry.CommitChanges();
                userEntry.Dispose();
                dirEntry.Dispose();
                result = true;

            }
            catch (System.Reflection.TargetInvocationException exTIX)
            {
                result = false;
                throw exTIX.InnerException;
            }
            catch (Exception ex)
            {
                result = false;
                throw ex;
            }

            return result;
        }

        /// <summary>
        /// Gets a local user account
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public LocalUser GetAccount(string userName)
        {
            LocalUser lu = new LocalUser();

            using (DirectoryEntry dirEntry =
                    new DirectoryEntry("WinNT://" + computerName + ",computer"))
            {
                try
                {
                    using (DirectoryEntry de = dirEntry.Children.Find(userName, "user"))
                    {
                        lu.UserName = de.InvokeGet("Name").ToString();

                        lu.UserFullName = de.InvokeGet("FullName").ToString();
                        lu.UserDescription = de.InvokeGet("Description").ToString();
                        lu.PasswordLastSet = Convert.ToBoolean(de.InvokeGet("PasswordExpired"));

                        int combinedFlags = 0;
                        // it turns out "UserFlags" has a few attributes to itself.  See enum type AdsUserFlags 
                        // for more information
                        combinedFlags = (int)de.InvokeGet("UserFlags");

                        int j = combinedFlags & (int)AdsUserFlags.PasswordCannotChange;

                        //Console.WriteLine(j.ToString());
                        int passwordCannotChange = combinedFlags & (int)AdsUserFlags.PasswordCannotChange;
                        if (passwordCannotChange == (int)AdsUserFlags.PasswordCannotChange)
                            lu.PasswordChangeable = false;
                        else
                            lu.PasswordChangeable = true;

                        int passwordNeverExpires = combinedFlags & (int)AdsUserFlags.PasswordDoesNotExpire;
                        if (passwordNeverExpires == (int)AdsUserFlags.PasswordDoesNotExpire)
                            lu.PasswordExpires = false;
                        else
                            lu.PasswordExpires = true;

                        int accountDisabled = combinedFlags & (int)AdsUserFlags.AccountDisabled;
                        if (accountDisabled == (int)AdsUserFlags.AccountDisabled)
                            lu.AccountDisabled = true;
                        else
                            lu.AccountDisabled = false;

                        int accountLockedout = combinedFlags & (int)AdsUserFlags.AccountLockedOut;
                        if (accountLockedout == (int)AdsUserFlags.AccountLockedOut)
                            lu.AccountLocked = true;
                        else
                            lu.AccountLocked = false;

                        //Console.WriteLine(combinedFlags.ToString());
                        // AdsUserFlags userFlags = (AdsUserFlags) de.Properties["userAccountControl"].Value;
                        // Console.WriteLine("AdsUserFlags for {0}: {1}",de.Path, userFlags);

                        //foreach (string Key in de.Properties.PropertyNames)
                        //{
                        //    string sPropertyValues = String.Empty;
                        //    // now loop through all the values in the property;
                        //    // can be a multi-value property
                        //    foreach (object Value in de.Properties[Key])
                        //        sPropertyValues += Convert.ToString(Value) + ";";
                        //    // cut off the separator at the end of the value list
                        //    sPropertyValues = sPropertyValues.Substring(0, sPropertyValues.Length - 1);
                        //    // now add the property info to the property list
                        //    Console.WriteLine(Key + "=" + sPropertyValues);
                        //}
                    }
                    //lu.PasswordChangeable = Convert.ToBoolean(de.Properties["PasswordChangeable"].Value);
                    //lu.PasswordExpires = Convert.ToBoolean(de.Properties["PasswordExpires"].Value);
                    //lu.AccountDisabled = Convert.ToBoolean(de.InvokeGet("Disabled"));
                }
                catch (Exception ex)
                {
                    ErrorLogger.LogError(ex.Message, ex.StackTrace);
                }
            }
            return lu;
        }

        /// <summary>
        /// Adds a user to group
        /// </summary>
        /// <param name="grpName"></param>
        /// <returns></returns>
        public bool AddToGroup(string grpName, string userName)
        {
            bool result = false;

            try
            {
                DirectoryEntry dirEntry =
                        new DirectoryEntry("WinNT://" + computerName + ",computer");

                DirectoryEntry grpEntry = dirEntry.Children.Find(grpName, "group");

                DirectoryEntry userEntry = dirEntry.Children.Find(userName, "user");

                if (userEntry == null)
                    return result;
  
                if (grpEntry != null)
                {
                    grpEntry.Invoke("Add", new object[] { userEntry.Path.ToString() });
                    if (userEntry != null)
                        userEntry.Dispose();
                }
                // activate changes
                grpEntry.CommitChanges();
                grpEntry.Dispose();
                // close the directory
                dirEntry.Dispose();
                // return the value
                result = true;
            }
            catch (Exception ex)
            {
                result = false;
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
            }

            return result;
        }

        /// <summary>
        /// Remove user from group
        /// </summary>
        /// <param name="grpName"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool RemoveFromGroup(string grpName, string userName)
        {
            bool result = false;

            try
            {
                DirectoryEntry dirEntry =
                        new DirectoryEntry("WinNT://" + computerName + ",computer");

                DirectoryEntry grpEntry = dirEntry.Children.Find(grpName, "group");

                DirectoryEntry userEntry = dirEntry.Children.Find(userName, "user");

                if (grpEntry != null)
                {
                    grpEntry.Invoke("Remove", new object[] { userEntry.Path.ToString() });
                }
                // activate changes
                grpEntry.CommitChanges();
                // dispose 
                userEntry.Dispose();
                grpEntry.Dispose();
                dirEntry.Dispose();
                // return the value
                result = true;
            }
            catch (Exception ex)
            {
                result = false;
                throw ex;
            }

            return result;
        }

        public DataSet GetBlankUserInfo()
        {
            CreateUserInfoTable();
            DataSet ds = userInfo.Clone();

            return ds;
        }

        #region "Helper methods"

        public LocalUser GetLocalUserByDataRow(DataRow row, string pswd)
        {
            LocalUser lu = new LocalUser();

            lu.UserName = row["UserName"].ToString();
            lu.UserFullName = row["FullName"].ToString();
            lu.UserPswd = pswd;
            lu.UserDescription = row["Description"].ToString();
            lu.PasswordLastSet = Convert.ToBoolean(row["PasswordLastSet"]);
            lu.PasswordChangeable = Convert.ToBoolean(row["PasswordChangeable"]);
            lu.AccountDisabled = Convert.ToBoolean(row["AccountDisabled"]);

            return lu;

        }

        // checks for a valid directory entry
        private bool CheckforValidDirectoryEntry(string dirEntryString)
        {
            bool result = false;
            try
            {
                if (DirectoryEntry.Exists(dirEntryString))
                    result = true;
                else
                    result = false;
            }
            catch (Exception ex)
            {
                result = false;
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
            }
            return result;
        }

        /// <summary>
        /// Check for an existing user on target system
        /// </summary>
        /// <param name="lu"></param>
        /// <param name="dirEntry"></param>
        /// <returns></returns>
        private bool CheckForExistingUser(LocalUser lu, DirectoryEntry dirEntry)
        {
            bool userFound = false;
            string userErr = string.Empty;
            try
            {
                if (dirEntry.Children.Find(lu.UserName, "user") != null)
                    userFound = true;
            }
            catch
            {
                userFound = false;
            }

            return userFound;
        }

        // create an in-memory dataset with the data proper column name and types
        private void CreateUserInfoTable()
        {
            userInfo = new DataSet();
            // set the dataset name
            userInfo.DataSetName = "userInfo";
            // instantiate a data table object
            DataTable dt = new DataTable();
            dt.TableName = "users";
            // create the columns
            dt.Columns.Add("UserName", System.Type.GetType("System.String"));
            dt.Columns["UserName"].AllowDBNull = false;
            dt.Columns.Add("FullName", System.Type.GetType("System.String"));
            dt.Columns.Add("Description", System.Type.GetType("System.String"));
            dt.Columns.Add("Pswd", System.Type.GetType("System.String"));
            dt.Columns.Add("ChangePswd", System.Type.GetType("System.String"));
            dt.Columns.Add("PasswordLastSet", System.Type.GetType("System.Boolean"));
            dt.Columns.Add("PasswordChangeable", System.Type.GetType("System.Boolean"));
            dt.Columns.Add("PasswordExpires", System.Type.GetType("System.Boolean"));
            dt.Columns.Add("AccountDisabled", System.Type.GetType("System.Boolean"));
            dt.Columns.Add("AccountLocked", System.Type.GetType("System.Boolean"));
            dt.Columns.Add("SID", System.Type.GetType("System.String"));

            // create the primary key for updating and finding rows in this dataset
            DataColumn[] dcPk = new DataColumn[1];
            // Set Primary Key
            dcPk[0] = dt.Columns["UserName"];
            dt.PrimaryKey = dcPk;
            dt.DefaultView.Sort = "UserName";
            // add the data table to the dataset
            userInfo.Tables.Add(dt);

            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            // keep for reference >>> from MSDN
            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            //UInt32 AccountType;
            //string Caption;
            //string Description;
            //bool  Disabled;
            //bool  Domain;
            //string FullName;
            //DateTime  InstallDate;
            //bool LocalAccount;
            //bool Lockout;
            //string Name;
            //bool PasswordChangeable;
            //bool PasswordExpires;
            //bool PasswordRequired;
            //string SID;
            //uint SIDType;
            //string Status;
            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

        }

        // fetch all the users from the target system
        private void FetchAllUsers()
        {
            string selectQuery = string.Empty;
            // if-clause to check whether the execution is on done on local computer
            //if (System.Environment.MachineName.Equals(computerName.ToUpper()))
            //{
            // for localmachine - note that "SelectQuery" value is different than local machine
            //SelectQuery sQuery = new SelectQuery("Win32_UserAccount", "Domain='" + computerName + "'");
            //}
            //else
            //{
            selectQuery = string.Format("SELECT * FROM Win32_UserAccount WHERE Domain='{0}'", computerName);

            //}
            ManagementObjectSearcher mSearcher = GetManagementObject(selectQuery);
            // call the method to add users to the in-memory dataset
            if (mSearcher != null)
                AddUsersToDataset(mSearcher);
        }

        private void FetchLockedUsers()
        {
            string selectquery = string.Format(
                        "SELECT * FROM Win32_UserAccount  WHERE Domain='{0}' AND Lockout = -1",
                        computerName.ToUpper());
            ManagementObjectSearcher mSearcher = GetManagementObject(selectquery);
            if (mSearcher != null)
                AddUsersToDataset(mSearcher);
        }

        private ManagementObjectSearcher GetManagementObject(string selectQuery)
        {
            ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(); ;
            try
            {
                // if-clause to check whether the execution is on done on local computer
                if (System.Environment.MachineName.Equals(computerName.ToUpper()))
                {
                    // for localmachine - note that "SelectQuery" value is different than local machine
                    //  SelectQuery sQuery = new SelectQuery("Win32_UserAccount", "Domain='" + computerName + "'");
                    SelectQuery sQuery = new SelectQuery(selectQuery);
                    mSearcher = new ManagementObjectSearcher(sQuery);
                    return mSearcher;
                }
                else
                {
                    // for remote machine -- need to change scope
                    string scopeString = String.Format("\\\\{0}\\root\\cimv2", computerName);
                    ManagementScope scope = new ManagementScope(scopeString);
                    SelectQuery sQuery = new SelectQuery(selectQuery);
                    scope.Connect();
                    mSearcher = new ManagementObjectSearcher(scope, sQuery);
                    return mSearcher;
                }
            }
            catch (Exception ex)
            {
                ErrorLogger.LogError(ex.Message, ex.StackTrace);
                return mSearcher;
            }
        }

        // adds the fetched users into the dataset
        private void AddUsersToDataset(ManagementObjectSearcher mSearcher)
        {
            try
            {
                foreach (ManagementObject mObject in mSearcher.Get())
                {
                    // ListManagementObjectProperties(mObject);
                    string description = mObject["Description"].ToString();
                    string userName = mObject["Name"].ToString();
                    // Do not mess with these default accounts - Filter them out!
                    if ((description.StartsWith("Built-in")) ||
                        (userName.Equals("Guest")) ||
                        (userName.StartsWith("ASPNET")) ||
                        (userName.StartsWith("IUSR_")) ||
                        (userName.StartsWith("IWAM_")))
                    {
                        continue;
                    }
                    else
                    {
                        DataRow row;        // every other account is fair game otherwise
                        row = userInfo.Tables[0].NewRow();
                        // fill in the attribute values
                        row["UserName"] = mObject["Name"].ToString();                                   // UserName
                        row["FullName"] = mObject["FullName"].ToString();                               // FullName
                        row["Description"] = mObject["Description"].ToString();                          // Description
                        // this attribute is not available within the ManagementObject properties
                        row["PasswordLastSet"] = ReadPasswordLastSet(row["UserName"].ToString());
                        row["AccountLocked"] = Convert.ToBoolean(mObject["Lockout"]);                   // Lockout
                        row["AccountDisabled"] = Convert.ToBoolean(mObject["Disabled"]);                // Disabled
                        row["PasswordChangeable"] = Convert.ToBoolean(mObject["PasswordChangeable"]);   // PasswordChangeable
                        row["PasswordExpires"] = Convert.ToBoolean(mObject["PasswordExpires"]);         // PasswordExpires
                        row["SID"] = mObject["SID"].ToString();                                         // SID
                        userInfo.Tables[0].Rows.Add(row);
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;           // throw the error up the stack
            }
        }

        private bool ReadPasswordLastSet(string userName)
        {
            bool result = false;
            DirectoryEntry dirEntry =
                    new DirectoryEntry("WinNT://" + computerName + ",computer");
            // since the management object does not give this attribute value,
            // have to use this method
            DirectoryEntry de = dirEntry.Children.Find(userName, "user");
            result = Convert.ToBoolean(de.InvokeGet("PasswordExpired"));
            de.Dispose();
            return result;
        }

        #endregion

        #region "Unused Utility Code"

        // [Utility()]
        private void ListManagementObjectProperties(ManagementObject mObject)
        {
            mObject.Options.UseAmendedQualifiers = true;
            // Console.WriteLine(mObject.Properties.Count);
            //Console.WriteLine(mObject["AccountDisable"]);
            // Console.WriteLine(mObject["SID"]);
            // Get the properties in the class
            PropertyDataCollection properties =
                mObject.Properties;

            // display the properties
            Console.WriteLine("Win32_User Account Property Names: ");
            foreach (PropertyData property in properties)
            {
                if (mObject[property.Name] != null)
                    Console.WriteLine("PropertyName: {0}\t Value: {1}", property.Name, mObject[property.Name].ToString());
                else
                    Console.WriteLine("PropertyName: {0}", property.Name);

                //foreach (QualifierData q in property.Qualifiers)
                //{
                //if (q.Name.Equals("Description"))
                //{
                //Console.WriteLine(
                //    mObject.GetPropertyQualifierValue(
                //    property.Name, q.Name));
                //}
                //}
            }

        }

        // not used - take it off for release
        private void CheckProperties()
        {

            // // Build a query for enumeration of Win32_Environment instances
            //// SelectQuery query = new SelectQuery("Win32_Environment");
            // SelectQuery query = new SelectQuery("Win32_UserAccount", "Domain='" + computerName + "'");

            // // Instantiate an object searcher with this query
            // ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

            // // Call Get() to retrieve the collection of objects and loop through it
            // foreach (ManagementBaseObject envVar in searcher.Get())
            //     Console.WriteLine("Variable : {0}, Value = {1}",
            //        envVar["Name"], envVar["VariableValue"]);
        }
        #endregion


    }
}
